package bm21;

/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 * @date 2023/8/28 22:14
 * BM21. 旋转数组中的最小数字
 * https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=295&tqId=23269&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj
 */
public class Solution {

    /**
     * 排序数组的查找问题首先考虑使用 二分法 解决，其可将 遍历法 的 线性级别 时间复杂度降低至 对数级别
     * 算法流程：
     * 1、初始化： 声明 i, j 双指针分别指向 array 数组左右两端
     * 2、循环二分： 设 m = (i + j) / 2 为每次二分的中点（ "/" 代表向下取整除法，因此恒有 i≤m1、当 array[m] > array[j] 时： m 一定在 左排序数组 中，即旋转点 x 一定在 [m + 1, j] 闭区间内，因此执行 i = m + 1
     * 2、当 array[m] < array[j] 时： m 一定在 右排序数组 中，即旋转点 x 一定在[i, m]闭区间内，因此执行 j = m
     * 3、当 array[m] = array[j] 时： 无法判断 m 在哪个排序数组中，即无法判断旋转点 x 在 [i, m] 还是 [m + 1, j] 区间中。解决方案： 执行 j = j - 1 缩小判断范围
     * 3、返回值： 当 i = j 时跳出二分循环，并返回 旋转点的值 array[i] 即可。
     */
    public int minNumberInRotateArray (int[] nums) {
        int i = 0, j = nums.length - 1;
        while (i < j) {
            int m = (i + j) / 2;
            if (nums[m] > nums[j]) {
                // m 在左排序数组中, 因此, 旋转点一定在 [m + 1, j] 区间
                i = m + 1;
            } else if (nums[m] < nums[j]) {
                // m 在右排序数组中, 因此, 旋转点一定在 [i, m]
                j = m;
            } else {
                j = j - 1; // 缩小范围
            }
        }
        return nums[i];
    }
}
